from AStock.ASEvent import Worker
from AStock.ASSetting import settings, cons
from AStock.ASUtil import date_to_bar_format, date_from_bar_format, ASHttpParser
from AStock.ASData import financial_dict
import json
from pymongo import UpdateOne


class ASFinancialNoticeDateCrawler(Worker):

    def __init__(self):
        super(ASFinancialNoticeDateCrawler, self).__init__()
        self._date_mapping = None
        self._proxy = None
        self._handler = None
        if settings.financial_source_notice_date == cons.CrawlSource.EASTMONEY:
            self._url = cons.EASTMONEY_FINANCIAL_NOTICE_DATE
            self._date_mapping = self._eastmoney_date
            self._handler = self._eastmoney_text_handler
        else:
            raise NotImplementedError

    @staticmethod
    def _eastmoney_date(date):
        return date_to_bar_format(date)

    @staticmethod
    def _eastmoney_text_handler(text, **kwargs):
        """
        解析响应的文本
        :param text: str，待解析的响应文本
        :return: dict，
            失败时，None
            成功时，{'page_no': 1, 'pages': 105, 'data': [(code, notice_date), (code, notice_date), ...]}
        """
        s = 'var BSoAyPHj='  # 与URL中的var参数一致
        # s = 'var MYcOmBoi='  # 与URL中的var参数一致
        if not text.startswith(s) or len(text) <= len(s):
            return None
        charset = kwargs.get('charset', 'utf-8')
        try:
            js = json.loads(text[len(s):-1], encoding=charset)
        except json.JSONDecodeError as e:
            print('error parse eastmoney financial notice date: {}'.format(e.msg))
            return None

        try:
            res = {'page_no': kwargs.get('page_no'), 'pages': None, 'data': []}
            if js['success']:
                result = js['result']
                res['pages'] = result['pages']
                for item in result['data']:
                    try:
                        notice_date = date_from_bar_format(item['NOTICE_DATE'][:len('2020-03-31')])
                        # notice_date = date_from_bar_format(item['ACTUAL_PUBLISH_DATE'][:len('2020-03-31')])
                        tup = (item['SECURITY_CODE'], notice_date)
                        res['data'].append(tup)
                    except:
                        print('{} cannot convert ACTUAL_PUBLISH_DATE, may be empty'.format(item['SECURITY_CODE']))
                return res
        except ValueError as e:
            print('error parse eastmoney financial notice date: {}'.format(e))
            return None
        return None
        
    def run(self, event_data):
        """
        执行抓取财务报表发布日期的任务
        :param event_data: dict，包括：
            event_type: financial_notice_date 抓取财务报表发布日期
            report_date: str，报表日期，格式为 yyyymmdd
            page_no: int，页码
            page_size: int，页大小
            rand: int，随机数

        :return: dict，
            失败时，返回的字典为 {'error': 'error string'}
            成功时，返回的字典为 {'page_no': 1, 'pages': 105, 'data': [(code, notice_date), (code, notice_date), ...]}
        """
        try:
            event_type = event_data['event_type']
            report_date = event_data['report_date']
            page_no = event_data['page_no']
            page_size = event_data['page_size']
            rand = event_data['rand']
        except KeyError as e:
            return {'error': 'key error in event data, key: {}'.format(e)}

        if event_type not in ['financial_notice_date']:
            return {'error': 'event type error: {}'.format(event_type)}

        if self._date_mapping:
            report_date = self._date_mapping(report_date)

        url = self._url.format(
            pageno=page_no, reportdate=report_date, pagesize=page_size, rand=rand)
        parser = ASHttpParser(url, cons.HEADER, self._proxy, content_type="text")
        res = parser.parse_text(text_handler=self._handler, charset=parser.charset, page_no=page_no)
        if res is None:
            return {'error': 'error crawl financial notice date, url: {}'.format(url)}
        return res


class ASFinancialNoticeDateSaver(Worker):

    def __init__(self):
        super().__init__()
        # 英文名-字段编号，后续如果需要调整字段编号对应的财务项目
        # 只需要将financial_dict中的英文名调整到对应的编号下即可
        self._financial_dict = {
            v: k[:3] for k, v in financial_dict.items()}

    def run(self, event_data):
        """
        执行保存财务报表发布日期的任务
        :param event_data: dict，包括：
            event_type: financial_notice_date 保存财务报表发布日期
            report_date: str，报表日期，格式为 yyyymmdd
            data: list，[(code, notice_date), (code, notice_date), ...]

        :return: dict，
            成功：{'succ': 'saved: 20200630    financial notice date'}
            失败：{'fail': 'failed to save: 20200630    financial notice date'}
        """
        # print(event_data)
        try:
            event_type = event_data['event_type']
            report_date = event_data['report_date']
            data = event_data['data']
        except KeyError as e:
            return {'fail': 'key error in event data, key: {}'.format(e)}

        if event_type not in ['financial_notice_date']:
            return {'fail': 'event type error: {}'.format(event_type)}

        succ = 'saved: {}   financial notice date'.format(report_date)
        fail = 'failed to save: {}   financial notice date'.format(report_date)

        bulk = []
        for item in data:
            update_one = UpdateOne(
                {'code': item[0], 'report_date': int(report_date)},
                # 注意日期类型在库里保存为int
                {'$set': {self._financial_dict['financialNoticeDate']: int(item[1])}},
                upsert=True
            )
            bulk.append(update_one)

        try:
            coll = settings.database.financial  # 文档集合对象
            coll.bulk_write(bulk)
        except Exception as e:
            return {'fail': fail + '    ' + str(e)}

        return {'succ': succ}

